using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson;

namespace MongoScienceDAL.Core
{
    public static class QueryCriteriaHelperExtensionMethods
    {
        //Filters
        #region General Helpers

        public static QueryCriteria AddDosentHaveField(this QueryCriteria criteria, string key)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DosentHaveField, Key = key };
            
            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddHasField(this QueryCriteria criteria, string key)
        {
            var filter = new QueryFilter { Type = QueryFilterType.HasField, Key = key };
            
            criteria.Filters.Add(filter);

            return criteria;
        }

        #endregion
        #region Text Helpers

        public static QueryCriteria AddTextContains(this QueryCriteria criteria, string key, string text)
        {
            var filter = new QueryFilter {Type = QueryFilterType.TextContains, Key = key};

            filter.Data["text"] = text;

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddTextStartsWith(this QueryCriteria criteria, string key, string text)
        {
            var filter = new QueryFilter { Type = QueryFilterType.TextStartsWith, Key = key };

            filter.Data["text"] = text;

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddTextEndsWith(this QueryCriteria criteria, string key, string text)
        {
            var filter = new QueryFilter { Type = QueryFilterType.TextEndsWith, Key = key };

            filter.Data["text"] = text;

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddTextEquals(this QueryCriteria criteria, string key, string text)
        {
            var filter = new QueryFilter { Type = QueryFilterType.TextEquals, Key = key };

            filter.Data["text"] = text;

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddTextRegex(this QueryCriteria criteria, string key, string text)
        {
            var filter = new QueryFilter { Type = QueryFilterType.TextRegex, Key = key };

            filter.Data["text"] = text;

            criteria.Filters.Add(filter);

            return criteria;
        }

        #endregion
        #region Date Helpers
        public static QueryCriteria AddDateEquals(this QueryCriteria criteria, string key, DateTime date)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DateEquals, Key = key };

            filter.Data.Add("date", date);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddDateGreaterThan(this QueryCriteria criteria, string key, DateTime date)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DateGreaterThan, Key = key };

            filter.Data.Add("date", date);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddDateGreaterThanOrEqualTo(this QueryCriteria criteria, string key, DateTime date)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DateGreaterThanOrEquals, Key = key };

            filter.Data.Add("date", date);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddDateLessThan(this QueryCriteria criteria, string key, DateTime date)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DateLessThan, Key = key };

            filter.Data.Add("date", date);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddDateLessThanOrEqualTo(this QueryCriteria criteria, string key, DateTime date)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DateLessThanOrEquals, Key = key };

            filter.Data.Add("date", date);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddDateInsideRange(this QueryCriteria criteria, string key, DateTime startdate, DateTime enddate)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DateInsideRange, Key = key };

            filter.Data.Add("startdate", startdate);
            filter.Data.Add("enddate", enddate);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddDateOutsideRange(this QueryCriteria criteria, string key, DateTime startdate, DateTime enddate)
        {
            var filter = new QueryFilter { Type = QueryFilterType.DateOutsideRange, Key = key };

            filter.Data.Add("startdate", startdate);
            filter.Data.Add("enddate", enddate);

            criteria.Filters.Add(filter);

            return criteria;
        }

        #endregion
        #region Value Helpers

        public static QueryCriteria AddValueEquals(this QueryCriteria criteria, string key, double value)
        {
            var filter = new QueryFilter {Type = QueryFilterType.ValueEquals, Key = key};

            filter.Data.Add("value", value);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddValueGreaterThan(this QueryCriteria criteria, string key, double value)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ValueGreaterThan, Key = key };

            filter.Data.Add("value", value);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddValueGreaterThanOrEqualTo(this QueryCriteria criteria, string key, double value)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ValueGreaterThanOrEquals, Key = key };

            filter.Data.Add("value", value);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddValueLessThan(this QueryCriteria criteria, string key, double value)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ValueLessThan, Key = key };

            filter.Data.Add("value", value);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddValueLessThanOrEqualTo(this QueryCriteria criteria, string key, double value)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ValueLessThanOrEquals, Key = key };

            filter.Data.Add("value", value);

            criteria.Filters.Add(filter);

            return criteria;
        }

        #endregion
        #region ObjectId Helpers

        public static QueryCriteria AddObjectIdEquals(this QueryCriteria criteria, ObjectId id)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ObjectIdEquals, Key = "_id" };

            filter.Data.Add("value", id);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddObjectIdEqualsAny(this QueryCriteria criteria, IList<ObjectId> ids)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ObjectIdEqualsAny, Key = "_id" };

            filter.Data.Add("value", ids);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddParentIdEquals(this QueryCriteria criteria, string key, ObjectId id)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ParentIdEquals, Key = key };

            filter.Data.Add("value", id);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddParentIdEqualsAny(this QueryCriteria criteria, string key, IList<ObjectId> ids)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ParentIdEqualsAny, Key = key };

            filter.Data.Add("value", ids);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddChildIdsContain(this QueryCriteria criteria, string key, ObjectId id)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ChildIdsContain, Key = key };

            filter.Data.Add("value", id);

            criteria.Filters.Add(filter);

            return criteria;
        }

        public static QueryCriteria AddChildIdsContainAny(this QueryCriteria criteria, string key, IList<ObjectId> ids)
        {
            var filter = new QueryFilter { Type = QueryFilterType.ChildIdsContainAny, Key = key };

            filter.Data.Add("value", ids);

            criteria.Filters.Add(filter);

            return criteria;
        }

        #endregion
        #region Boolean Helpers

        public static QueryCriteria AddBooleanEquals(this QueryCriteria criteria, string key, bool value)
        {
            var filter = new QueryFilter { Type = QueryFilterType.BooleanEquals, Key = key };

            filter.Data.Add("value", value);

            criteria.Filters.Add(filter);

            return criteria;
        }

        #endregion
        
        //Selectors
        #region Selector Helpers

        public static QueryCriteria AddSelector(this QueryCriteria criteria, string key)
        {
            //if it is already added
            if (criteria.Selectors.Select(x => x.Key).Contains(key)) return criteria;

            //if _id or _col we ignore
            if (key == "_id" || key == "_col") return criteria;

            //add and return
            criteria.Selectors.Add(new QuerySelector {Key = key});
            return criteria;
        }

        #endregion
    }
}